use crate::modbus::basic::{ModbusRtu, ModbusRtuResult};
use crate::common::models::{Soil, Weather, Sleet};
use crate::common::cs;
use crate::common::cs::Error;
use crate::util;
use crate::modbus::{READ_HOLD_REGISTER, WRITE_SINGLE_HOLD_REGISTER, READ_COIL_REGISTER, WRITE_SINGLE_COIL_REGISTER};

/// soil抓取数据的命令
pub fn soil_get_order(address: u8) -> [u8; 8] {
    ModbusRtu::from(&[address, READ_HOLD_REGISTER, 0x00, 0x00, 0x00, 0x08]).to_slice()
}

/// weather抓取数据的命令
pub fn weather_get_order(address: u8) -> [u8; 8] {
    ModbusRtu::from(&[address, READ_HOLD_REGISTER, 0x01, 0xf4, 0x00, 0x08]).to_slice()
}

/// weather初始化取消虚拟地址
pub fn weather_init_order(address: u8) -> [u8; 8] {
    ModbusRtu::from(&[address, WRITE_SINGLE_HOLD_REGISTER, 0x00, 0x60, 0x00, 0x01]).to_slice()
}

/// sleet抓取数据的命令
pub fn sleet_get_order(address: u8) -> [u8; 8] {
    ModbusRtu::from(&[address, READ_HOLD_REGISTER, 0x00, 0x00, 0x00, 0x01]).to_slice()
}

/// switch获取状态命令
pub fn switch_get_order(address: u8) -> [u8; 8] {
    ModbusRtu::from(&[address, READ_COIL_REGISTER, 0x00, 0x00, 0x00, 0x01]).to_slice()
}

/// switch设置开关状态
pub fn switch_set_open(address: u8, open: bool) -> [u8; 8] {
    let open_order = if open { 0xff } else { 0x00 };
    ModbusRtu::from(&[address, WRITE_SINGLE_COIL_REGISTER, 0x00, 0x00, open_order, 0x00]).to_slice()
}

/// 集成开关设置状态
pub fn paralleling_set(address: &str, access: u8, open: bool) -> Option<[u8; 9]> {
    match util::s2v(address) {
        None => { None }
        Some(v) => {
            if v.len() < 5 {
                return None;
            }
            let open = if open { 0x01 } else { 0x00 };
            Some([
                *v.get(0).unwrap(),
                *v.get(1).unwrap(),
                *v.get(2).unwrap(),
                *v.get(3).unwrap(),
                *v.get(4).unwrap(),
                0x00, access, open, 0x0f
            ])
        }
    }
}

/// 集成开关获取状态
pub fn paralleling_get_order(address: &str) -> Option<[u8; 9]> {
    match util::s2v(address) {
        None => { None }
        Some(v) => {
            if v.len() < 5 {
                return None;
            }
            Some([
                *v.get(0).unwrap(),
                *v.get(1).unwrap(),
                *v.get(2).unwrap(),
                *v.get(3).unwrap(),
                *v.get(4).unwrap(),
                0x86, 0x86, 0x86, 0x0f
            ])
        }
    }
}

pub fn paralleling_set_all(address: &str, open: bool) -> Option<[u8; 9]> {
    match util::s2v(address) {
        None => { None }
        Some(v) => {
            if v.len() < 5 {
                return None;
            }
            let open = if open { 0x01 } else { 0x02 };
            Some([
                *v.get(0).unwrap(),
                *v.get(1).unwrap(),
                *v.get(2).unwrap(),
                *v.get(3).unwrap(),
                *v.get(4).unwrap(),
                open, 0x77, 0x88, 0x0f
            ])
        }
    }
}

/// 解析soil的结果并映射到实体
pub fn analysis_soil(mr: &ModbusRtuResult) -> cs::Result<Soil> {
    let mut result = Soil::new(mr.address as u32);
    let err = || { Error::new(format!("parse [soil] from modbus result : `{:?}`", mr)) };
    result.moisture = get_data_u16(mr, 0, err)? as f32 / 10.0;
    result.temperature = get_data_u16(mr, 2, err)? as f32 / 10.0;
    result.electrical = get_data_u16(mr, 4, err)? as u32;
    result.nitrogen = get_data_u16(mr, 6, err)? as u32;
    result.phosphorus = get_data_u16(mr, 8, err)? as u32;
    result.potassium = get_data_u16(mr, 10, err)? as u32;
    result.salinity = get_data_u16(mr, 12, err)? as u32;
    result.tds = get_data_u16(mr, 14, err)? as u32;
    Ok(result)
}

/// 解析weather的结果并映射到实体
pub fn analysis_weather(mr: &ModbusRtuResult) -> cs::Result<Weather> {
    let mut result = Weather::new(mr.address as u32);
    let err = || { Error::new(format!("parse [weather] from modbus result : `{:?}`", mr)) };
    result.humidity = get_data_u16(mr, 0, err)? as f32 / 10.0;
    result.temperature = get_data_u16(mr, 2, err)? as f32 / 10.0;
    result.noise = get_data_u16(mr, 4, err)? as f32 / 10.0;
    result.pm25 = get_data_u16(mr, 6, err)? as u32;
    result.pm10 = get_data_u16(mr, 8, err)? as u32;
    Ok(result)
}

/// 解析sleet的结果并映射到实体
pub fn analysis_sleet(mr: &ModbusRtuResult) -> cs::Result<Sleet> {
    let mut result = Sleet::new(mr.address as u32);
    let err = || { Error::new(format!("parse [sleet] from modbus result : `{:?}`", mr)) };
    result.status = get_data_u16(mr, 0, err)? == 0x0001;
    Ok(result)
}

/// 解析switch的结果
pub fn analysis_switch(mr: &ModbusRtuResult) -> cs::Result<bool> {
    match (mr.data.len(), mr.data.get(0)) {
        (1, Some(u)) => { Ok(1.eq(u)) }
        (_, _) => { Err(Error::new(format!("parse [sleet] from modbus result : `{:?}`", mr))) }
    }
}

/// 获取u16的数据
fn get_data_u16<F>(mr: &ModbusRtuResult, index: usize, err: F) -> cs::Result<u16>
    where F: Fn() -> cs::Error
{
    let data_l = *mr.data.get(index).ok_or_else(&err)?;
    let data_r = *mr.data.get(index + 1).ok_or_else(&err)?;
    Ok(util::double_u8_to_u16(data_l, data_r))
}

/// 预留 获取u8数据
fn _get_data_u8<F>(mr: &ModbusRtuResult, index: usize, err: F) -> cs::Result<u8>
    where F: Fn() -> cs::Error
{
    Ok(*mr.data.get(index).ok_or_else(&err)?)
}

#[cfg(test)]
mod tests {
    use crate::common::orders;
    use crate::common::orders::paralleling_get_order;

    #[test]
    fn test1() {
        let order = orders::switch_get_order(0x02);
        println!("{:?}", order);
    }

    #[test]
    fn test2() {
        let option = orders::paralleling_set("aaaaaaaaaa", 0, true);
        println!("{:?}", option);
    }

    #[test]
    fn test3() {
        let option = paralleling_get_order("1A18120206");
        println!("{:x?}", option);
    }
}